#include "chatservice.hpp"
#include "public.hpp"
#include "user.hpp"
#include <muduo/base/Logging.h>
#include <vector>
using namespace std;
using namespace muduo;
// 获取单例对象的接口函数
ChatService *ChatService::instance()
{
    static ChatService service;
    return &service;
}

// 注册消息，以及对应的Handler回调
ChatService::ChatService()
{
    // 用户
    _msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::login, this, _1, _2, _3)});
    _msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::reg, this, _1, _2, _3)});
    _msgHandlerMap.insert({LOGIN_OUT_MSG, std::bind(&ChatService::loginout, this, _1, _2, _3)});
    _msgHandlerMap.insert({ONE_CHAT_MEG, std::bind(&ChatService::oneChat, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_FRIEND_MSG, std::bind(&ChatService::addFriend, this, _1, _2, _3)});
    // 群组
    _msgHandlerMap.insert({CREATE_GROUP_MSG, std::bind(&ChatService::createGroup, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_GROUP_MSG, std::bind(&ChatService::addGroup, this, _1, _2, _3)});
    _msgHandlerMap.insert({GROUP_CHAT_MSG, std::bind(&ChatService::groupChat, this, _1, _2, _3)});

    // redis
    if (_redis.connect())
    {
        cout<<"redis连接成功"<<endl;
        // 设置redis订阅消息的处理方法
        _redis.init_notify_handler(std::bind(&ChatService::handleRedisSubscribeMessage, this, _1, _2));
    }
}
//redis订阅消息处理函数
void ChatService::handleRedisSubscribeMessage(int channel, string message)
{
    cout<<"处理订阅的消息"<<endl;
    //这里的管道channel就是用户的id
    lock_guard<mutex> lock(_connMutex);
    auto it = _userConnMap.find(channel);
    if(it != _userConnMap.end()){
        it->second->send(message);
        return;
    }
    //有可能这个用户现在已经不在线了
    _offlineMsgModel.insert(channel,message);
    
}
// 登入
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int id = js["id"].get<int>();
    string pwd = js["password"];
    // 查询数据库去了
    User user = _userModel.query(id);
    if (user.getId() == id && user.getPwd() == pwd)
    {
        if (user.getState() == "online")
        {
            // 已经登入了
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["error"] = 2;
            response["errmsg"] = "this account is using!";
            conn->send(response.dump());
        }
        else
        {
            // 登入成功,更新用户状态信息为online
            user.setState("online");
            _userModel.updateState(user);
            // 记录用户连接信息
            {
                lock_guard<mutex> lock(_connMutex);
                _userConnMap.insert({id, conn});
            }
            // 登入成功后，在redis订阅channel，号码为id号
            _redis.subscribe(id);
            cout<<"redis-channel订阅成功"<<endl;

            // 向客户端返回登入成功的json数据包
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["error"] = 0;
            response["id"] = user.getId();
            response["name"] = user.getName();
            // 查询该用户是否有离线消息
            vector<string> vec = _offlineMsgModel.query(id);
            if (!vec.empty())
            {
                // vector容器也可以参与dump的序列化，所以可以直接当作json数据对象传入
                response["offlinemsg"] = vec;
                // 读取完离线消息后，将其所有离线消息删除
                _offlineMsgModel.remove(id);
            }
            // 查询该用户的好友信息并返回
            vector<User> Uservec = _friendModel.query(id);
            if (!Uservec.empty())
            {
                vector<string> vec2;
                for (User &user : Uservec)
                {
                    json js;
                    js["id"] = user.getId();
                    js["name"] = user.getName();
                    js["state"] = user.getState();
                    vec2.push_back(js.dump());
                }
                response["friends"] = vec2;
            }
            // 查询该用户的群组信息并返回
            vector<Group> groupVec = _groupmodel.queryGroups(id);
            if (!groupVec.empty())
            {
                vector<string> vec3;
                vector<string> groupUservec;
                for (Group &group : groupVec)
                {

                    json js;
                    js["id"] = group.getId();
                    js["groupname"] = group.getName();
                    js["groupdesc"] = group.getDesc();
                    for (GroupUser &guser : group.getUsers())
                    {
                        json js2;
                        js2["id"] = guser.getId();
                        js2["name"] = guser.getName();
                        js2["state"] = guser.getState();
                        js2["role"] = guser.getRole();
                        groupUservec.push_back(js2.dump());
                    }
                    js["users"] = groupUservec;
                    vec3.push_back(js.dump());
                }
                response["groups"] = vec3;
            }

            conn->send(response.dump());
        }
    }
    else
    {
        // 用户不存在或密码错误
        json response;
        response["msgid"] = LOGIN_MSG_ACK;
        response["error"] = 1;
        response["errmsg"] = "id or password invalid";
        conn->send(response.dump());
    }
}
// 注册
void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    string name = js["name"];
    string pwd = js["password"];
    User user;
    user.setName(name);
    user.setPwd(pwd);
    bool state = _userModel.insert(user);
    if (state)
    {
        // 构建向客户端返回的json数据包
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["error"] = 0;
        response["id"] = user.getId();
        conn->send(response.dump());
    }
    else
    {
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["error"] = 1;
        conn->send(response.dump());
    }
}
//用户注销
void ChatService::loginout(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    {
        lock_guard<mutex> lock(_connMutex);
        auto it = _userConnMap.find(userid);
        // 将注销的用户移出用户连接表
        if (it != _userConnMap.end())
        {
            _userConnMap.erase(it);
        }
    }
    _redis.unsubscribe(userid);
    // 操作数据库，更新用户状态
    User user(userid, " ", " ", "offline");
    _userModel.updateState(user);
}
// 在_msgHandlerMap中查找消息id对应的消息处理器
MsgHandler ChatService::getHandler(int msgid)
{
    // 记录错误日志：没有对应的消息类型处理
    auto it = _msgHandlerMap.find(msgid);
    if (it == _msgHandlerMap.end())
    {
        // 返回一个空操作.为了解耦合，不在chatservice中处理业务层的异常，而是返回
        // 一个空handler
        return [msgid](const TcpConnectionPtr &conn, json &js, Timestamp time)
        {
            LOG_ERROR << "msgid:" << msgid << "can not find handler";
        };
    }
    else
    {
        return _msgHandlerMap[msgid];
    }
}
// 处理异常退出的用户
void ChatService::clientCloseException(const TcpConnectionPtr &conn)
{
    cout << "处理异常退出的用户" << endl;
    User user;
    {
        cout << "进入这个代码块" << endl;
        lock_guard<mutex> lock(_connMutex);
        cout << "开始寻找这个用户" << endl;
        for (auto it = _userConnMap.begin(); it != _userConnMap.end(); it++)
        {
            if (it->second == conn)
            {
                cout << "断开的用户为" << it->first << endl;
                user.setId(it->first);
                // 从map表删除用户的连接信息
                cout << "从 _userConnMap中删除用户的连接信息" << endl;
                _userConnMap.erase(it);
                break;
            }
        }
    }
    // 异常退出时，也需要对redis进行取消订阅
    _redis.unsubscribe(user.getId());
    if (user.getId() != -1)
    {
        cout << "更新用户状态" << endl;
        // 更新用户状态
        user.setState("offline");
        _userModel.updateState(user);
    }
    else
    {
        cout << "没有找到这个用户" << endl;
    }
}
// 一对一聊天
void ChatService::oneChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    // 对方的id号
    int toid = js["toid"].get<int>();
    {
        lock_guard<mutex> lock(_connMutex);
        // 是否在当前的主机
        auto it = _userConnMap.find(toid);
        if (it != _userConnMap.end())
        {
            // 在线,发送消息;
            it->second->send(js.dump());
            return;
        }
    }
    // 是否在远程的主机
    User user = _userModel.query(toid);
    if (user.getState() == "online")
    {
        cout<<"该用户在远程主机"<<endl;
        // 在远程主机，向其channel发送消息
        _redis.publish(toid, js.dump());
        cout<<"向channel推送消息成功"<<endl;
        return;
    }
    // 不在线，存储离线消息
    _offlineMsgModel.insert(toid, js.dump());
}
// 服务器异常，重置业务
void ChatService::reset()
{
    // 把在线的用户状态state全部重置为offline
    _userModel.resetState();
}
// 添加好友
void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int friendid = js["friendid"].get<int>();
    // 存储好友信息
    _friendModel.insert(userid, friendid);
}
// 创建群组
void ChatService::createGroup(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    string name = js["groupname"];
    string desc = js["groupdesc"];
    Group group(-1, name, desc);
    // 创建群
    if (_groupmodel.createGroup(group))
    {
        // 作为群主，加入群
        _groupmodel.addGroup(userid, group.getId(), "creator");
    }
}
// 加入群组
void ChatService::addGroup(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();
    string role = "normal";
    _groupmodel.addGroup(userid, groupid, role);
}
// 群聊天
void ChatService::groupChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();
    // 查询群组内的所有成员，除去自己
    vector<int> userIdVec = _groupmodel.queryGroupUsers(userid, groupid);
    lock_guard<mutex> lock(_connMutex);
    for (int id : userIdVec)
    {
        auto it = _userConnMap.find(id);
        if (it != _userConnMap.end())
        {
            // 在当前服务器在线，发送消息
            it->second->send(js.dump());
        }
        else
        {
            // 查询是否在远程主机
            User user = _userModel.query(id);
            if (user.getState() == "online")
            {
                _redis.publish(id, js.dump());
            }
            else
            {
                // 不在线，存储在其离线消息中
                _offlineMsgModel.insert(id, js.dump());
            }
        }
    }
}
